﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Net.Sockets;

namespace TransportAbstractionLibrary.Common.BufferPool
{
    sealed class BufferInstance
    {
        object freeLocker;
        object inUseLocker;
        byte[] buffer;
        LinkedList<BufferLink> inUseList;
        LinkedList<BufferLink> freeList;
        int bufferSize;
        int count;
        int cleanups;

        internal BufferInstance(int bufferSize, int count)
        {
            inUseLocker = new object();
            freeLocker = new object();
            freeList = new LinkedList<BufferLink>();
            inUseList = new LinkedList<BufferLink>();
            buffer = new byte[bufferSize * count];
            GC.Collect();
            this.bufferSize = bufferSize;
            this.count = count;
            //skapa alla vi har
            for (int i = 0; i < count; i++)
            {
                BufferLink link = new BufferLink(i, this, bufferSize);
                freeList.AddLast(link);
            }
        }

        internal SocketEventArgs CheckOut()
        {
            lock(freeLocker)
            {
                Thread.MemoryBarrier();
                LinkedListNode<BufferLink> item = freeList.First;
                //We need to find a free item!
                if (item != null)
                {
                    freeList.Remove(item);
                    lock(inUseLocker)
                    {
                        inUseList.AddLast(item);
                    }
                    BufferLink link = item.Value;
                    link.Value = new SocketEventArgs(buffer, link.Index * bufferSize, bufferSize);
                    return link.Value;
                }
            }
            return null;
        }

        internal bool CheckIn(SocketEventArgs value)
        {
            lock(inUseLocker)
            {
                Thread.MemoryBarrier();
                LinkedListNode<BufferLink> item = inUseList.First;

                while (item != null)
                {
                    if (item.Value.Value == value)
                    {
                        //we got lucky here some one didnt clean up after them!
                        item.Value.Value = null;
                        inUseList.Remove(item);
                        freeList.AddFirst(item);
                        return true;
                    }
                    item = item.Next;
                }

            }
            return false;
        }

        internal void Cleanup()
        {
            lock(inUseLocker)
            {
                Thread.MemoryBarrier();
                LinkedListNode<BufferLink> item = inUseList.First;

                while (item != null)
                {
                    if (item.Value.Value == null)
                    {
                        //we got lucky here some one didnt clean up after them!
                        inUseList.Remove(item);
                        freeList.AddFirst(item);
                        Interlocked.Add(ref cleanups, 1);
                    }
                    item = item.Next;
                }
            }
        }
    }
}
